import java.util.*;

public class RandomProjection
{
	Double[][] projection;
	private int targetDimension;
//	private int sourceDimension;
	private Double scaleFactor;
	private Map<Integer, Integer> dict;

	// Initialize Random Projection Matrix
	public RandomProjection(int sourceDimension, int targetDimension, Map<Integer,Integer> dict)
	{
		System.out.println(sourceDimension + " " + targetDimension);
		this.targetDimension = targetDimension;
//		this.sourceDimension = sourceDimension;
		this.scaleFactor = Math.sqrt((double)sourceDimension / (double)targetDimension);
		this.dict = dict;

		Random randomizer = new Random();
		projection = new Double[targetDimension][];
		for (int i = 0; i < targetDimension; i++)
		{
			projection[i] = new Double[sourceDimension];
			for (int j = 0; j < sourceDimension; j++)
			{
				projection[i][j] = randomizer.nextGaussian();
			}
		}

		// Normalize to unit vector
		for (int i = 0; i < targetDimension; i++)
		{
			Double length = 0.0;
			for (int j = 0; j < sourceDimension; j++)
			{
				length += Math.pow(projection[i][j],2);
			}
			length = Math.sqrt(length);
			for (int j = 0; j < sourceDimension; j++)
			{
				projection[i][j] /= length;
			}
		}

	}

	// Project the points in database using projection matrix
	public Point[] doProjection(Point DataBase[])
	{
		int len = DataBase.length;
		Point[] projectedDB = new Point[len];
		for (int i = 0; i < len; i++)
		{
			HashMap<Object, Double> feature = new HashMap<Object, Double>();
			for (int j = 0; j < targetDimension; j++)
			{
				Double temp = 0d;
				if (DataBase[i].isDense())
				{
					System.out.println("Haven't implemented yet!");
					System.exit(1);
//					Double[] sourceFeature = DataBase[i].FeatureDense();
//					for (int k = 0; k < sourceDimension; k++) feature.put(j, feature.get(j) + sourceFeature[k] * projection[j][k]);
				}
				else
				{
					Map<Object, Double> sourceFeature = DataBase[i].FeatureSparse();
					for (Object key : sourceFeature.keySet())
					{
						temp = temp + sourceFeature.get(key) * projection[j][dict.get((Integer)key)];
					}
				}
				temp = temp * scaleFactor;
				feature.put(j, temp);
			}
			projectedDB[i] = new Point(DataBase[i].ID(), feature, DataBase[i].type());
		}
		return projectedDB;
	}
/*
	public Point doProjection(Point p)
	{
		Double feature[] = new Double[targetDimension];
		for (int j = 0; j < targetDimension; j++)
		{
			feature[j]  = 0.0;
			if (p.isDense())
			{
				Double[] sourceFeature = p.FeatureDense();
				for (int k = 0; k < sourceDimension; k++) feature[j] += sourceFeature[k] * projection.get(j).get(k);
			}
			else
			{
				Map<Object, Double> sourceFeature = p.FeatureSparse();
				for (Object key : sourceFeature.keySet())
				{
					feature[j] += sourceFeature.get(key) * projection.get(j).get(dict.get((Integer)key));
				}
			}
			feature[j] *= scaleFactor;
		}
		return new Point(p.ID(), feature, p.type());
	}
*/
	/*
	public String toString()
	{
//		StringBuilder sb = new StringBuilder("Projection Matrix : \n");
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < targetDimension; i++)
		{
			//sb.append("[ ");
			sb.append(projection[i][0]);
			for (int j = 1; j < sourceDimension; j++)
			{
				//sb.append(String.format("%1$.2f ", projection[i][j]));
				sb.append("," + projection[i][j]);	
			}
			//sb.append("]\n");
			sb.append("\n");
		}
		return sb.toString();
	}
	*/
}
